package com.aruio.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * @Description 分批处理器(适用于大数据场景)
 * @Date 2024/9/3 14:03
 * @Created by 10574
 */
public class BatchHandlerListener<T> extends AnalysisEventListener<T> {
    /**
     * 每批的处理行数
     */
    private static final int BATCH_NUMBER = 1000;
    private final int batchNumber;

    /**
     * 临时存储的excel数据
     */
    private List<T> data;
    private int rows, batch;
    private boolean validateSwitch = true;

    /**
     * 每批数据的业务逻辑处理器
     * 说明：如果业务方法会返回结果，可以将换成Function接口，同时类上新增一个类型参数
     */
    private final Consumer<List<T>> businessHandler;

    /**
     * 用于校验模板正确性的字段
     */
    private final Field[] fields;
    private final Class<T> clazz;

    public BatchHandlerListener(Class<T> clazz, Consumer<List<T>> handle) {
        this(clazz, handle, BATCH_NUMBER);
    }

    public BatchHandlerListener(Class<T> clazz, Consumer<List<T>> handle, int batchNumber) {
        this.clazz = clazz;
        this.fields = clazz.getDeclaredFields();
        this.businessHandler = handle;
        this.batchNumber = batchNumber;
        this.data = new ArrayList<>();
    }

    /*
     * 每成功解析一条excel数据行时触发
     * */
    @Override
    public void invoke(T row, AnalysisContext context) {
        data.add(row);
        if (data.size() >= batchNumber) {
            rows += data.size();
            batch++;
            businessHandler.accept(data);
            data = new ArrayList<>(batchNumber);
        }
    }

    /**
     * 处理最后一批数据
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (data.size() != 0) {
            rows += data.size();
            batch++;
            businessHandler.accept(data);
            data.clear();
        }
    }

    /*
     * 读取到excel头信息时触发，会将表头数据转为Map集合（用于校验导入的excel文件与模板是否匹配）
     *   注意点1：当前校验逻辑不适用于多行头模板（如果是多行头的文件，请关闭表头验证）；
     *   注意点2：使用当前监听器的导入场景，模型类不允许出现既未忽略、又未使用ExcelProperty注解的字段；
     * */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        if (validateSwitch) {
            ExcelUtils.validateExcelTemplate(headMap, clazz, fields);
        }
    }

    /**
     * 关闭excel头校验
     */
    public void offValidata() {
        validateSwitch = false;
    }

    public List<T> getData() {
        return data;
    }

    public int getRows() {
        return rows;
    }

    public int getBatch() {
        return batch;
    }
}
